package com.hadoop.project.gen.maodaligendata;

import com.hadoop.project.entity.hbase.HBaseUser;
import com.hadoop.project.entity.hbase.Tab;
import com.hadoop.project.entity.hbase.UserLog;
import com.hadoop.project.entity.hbase.Video;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 用于生成 HBase 测试数据的工具类。
 * 包含 Tab、User、Video 和 UserLog 数据。
 */
public class GenData {

    // 定义 UserLog 批量上传的大小
    private static final int USER_LOG_BATCH_SIZE = 5000; // 每次批量提交 5000 条 UserLog 数据

    /**
     * 生成数据并插入到指定的 HBase 表中。
     *
     * @param tableName HBase 表名
     * @throws Exception 如果发生 HBase 连接或操作错误
     */
    public static void gen(String tableName) throws Exception {

        // 使用 try-with-resources 确保 Connection 和 Table 在使用后能被正确关闭
        try (Connection conn = HBaseUtils.getConnection();
             Table table = conn.getTable(TableName.valueOf(tableName))) {

            System.out.println("开始生成数据...");

            // --- 配置生成数据量 ---
            // 这里改成你想生成的数据的大小
            Integer genVideoLen = 10000;      // 生成视频的数量
            Integer genUserLen = 1000;        // 生成用户的数量
            Integer genUserLogLen = 1000000;  // 生成用户日志的数量 (一百万)

            // 用于存储生成的 ID，方便后续关联
            List<String> tidList = new ArrayList<>(); // 存储 Tab ID
            List<String> uidList = new ArrayList<>(); // 存储 User ID
            List<String> vidList = new ArrayList<>(); // 存储 Video ID
            Map<String, String> vidMapFlip = new HashMap<>(); // 存储 Video ID 到其 flip 时长的映射

            // --- 1. 生成 Tab 数据 ---
            System.out.println("开始生成 Tab 数据...");
            for (int i = 0; i < GenConstant.tabList.length; i++) {
                String tid = "tid-" + (i + 1);
                Tab tab = Tab.builder().tid(tid).tname(GenConstant.tabList[i]).build();
                tidList.add(tab.getTid());
                // System.out.println(tab); // 调试输出，默认关闭

                // 创建 Put 对象用于插入数据
                // RowKey 使用 Tab ID
                Put put = new Put(Bytes.toBytes(tab.getTid()));
                // 添加列数据，列族为 "tab"
                put.addColumn(Bytes.toBytes("tab"), Bytes.toBytes("tid"), Bytes.toBytes(tab.getTid()));
                put.addColumn(Bytes.toBytes("tab"), Bytes.toBytes("tabName"), Bytes.toBytes(tab.getTname()));
                table.put(put); // 单条插入 Tab 数据（量少，无需批量）
            }
            System.out.println("Tab 数据生成完毕，共 " + tidList.size() + " 条。");
            // System.out.println("生成的 Tab ID 列表: " + tidList); // 调试输出

            // --- 2. 生成 User 数据 ---
            System.out.println("开始生成 User 数据 (" + genUserLen + " 条)...");
            for (int i = 0; i < genUserLen; i++) {
                HBaseUser user = HBaseUser.builder()
                        .uid(GenConstant.genUid(10))       // 生成用户ID
                        .nickName(GenConstant.genNickName())  // 生成昵称
                        .sex(GenConstant.genSex())          // 生成性别
                        .age(GenConstant.genAge())          // 生成年龄
                        .province(GenConstant.genProvince())  // 生成省份
                        .build();
                uidList.add(user.getUid());
                // System.out.println(user); // 调试输出

                // 创建 Put 对象，RowKey 使用 User ID
                Put put = new Put(Bytes.toBytes(user.getUid()));
                // 添加列数据，列族为 "user"
                put.addColumn(Bytes.toBytes("user"), Bytes.toBytes("uid"), Bytes.toBytes(user.getUid()));
                put.addColumn(Bytes.toBytes("user"), Bytes.toBytes("nickname"), Bytes.toBytes(user.getNickName()));
                put.addColumn(Bytes.toBytes("user"), Bytes.toBytes("sex"), Bytes.toBytes(user.getSex()));
                put.addColumn(Bytes.toBytes("user"), Bytes.toBytes("age"), Bytes.toBytes(user.getAge()));
                put.addColumn(Bytes.toBytes("user"), Bytes.toBytes("province"), Bytes.toBytes(user.getProvince()));
                table.put(put); // 单条插入 User 数据（量不大，亦可考虑批量，但此处保持简单）

                // 简单进度提示 (可选)
                if ((i + 1) % 100 == 0 || (i + 1) == genUserLen) {
                    System.out.print("\rUser 数据生成进度: " + (i + 1) + "/" + genUserLen);
                }
            }
            System.out.println("\nUser 数据生成完毕。");
            // System.out.println("生成的 User ID 列表: " + uidList.size() + " 条"); // 调试输出

            // --- 3. 生成 Video 数据 ---
            System.out.println("开始生成 Video 数据 (" + genVideoLen + " 条)...");
            for (int i = 0; i < genVideoLen; i++) {
                Video video = Video.builder()
                        .vid(GenConstant.genLongTypeVID(10)) // 生成视频ID
                        .tabs(GenConstant.genTabs(tidList))    // 从已生成的 Tab ID 中随机选几个
                        .author(GenConstant.genAuthor(uidList)) // 从已生成的 User ID 中随机选一个
                        .flip(GenConstant.genFlip())         // 生成视频时长 (flip)
                        .build();
                vidList.add(video.getVid());
                vidMapFlip.put(video.getVid(), video.getFlip()); // 存储 vid -> flip 映射，用于 UserLog
                // System.out.println(video); // 调试输出

                // 创建 Put 对象，RowKey 使用 Video ID
                Put put = new Put(Bytes.toBytes(video.getVid()));
                // 添加列数据，列族为 "video"
                put.addColumn(Bytes.toBytes("video"), Bytes.toBytes("vid"), Bytes.toBytes(video.getVid()));
                put.addColumn(Bytes.toBytes("video"), Bytes.toBytes("tabs"), Bytes.toBytes(video.getTabs()));
                put.addColumn(Bytes.toBytes("video"), Bytes.toBytes("author"), Bytes.toBytes(video.getAuthor()));
                put.addColumn(Bytes.toBytes("video"), Bytes.toBytes("flip"), Bytes.toBytes(video.getFlip()));
                table.put(put); // 单条插入 Video 数据（量不大，亦可考虑批量）

                // 简单进度提示 (可选)
                if ((i + 1) % 100 == 0 || (i + 1) == genVideoLen) {
                    System.out.print("\rVideo 数据生成进度: " + (i + 1) + "/" + genVideoLen);
                }
            }
            System.out.println("\nVideo 数据生成完毕。");
            // System.out.println("生成的 Video ID 列表: " + vidList.size() + " 条"); // 调试输出
            // 测试 vidMapFlip 是否正确 (调试输出)
            // for (Map.Entry<String, String> entry : vidMapFlip.entrySet()) {
            //     System.out.println("vid:" + entry.getKey() + "            flip:" + entry.getValue());
            // }

            // --- 4. 生成 UserLog 数据 (采用批量上传和进度条) ---
            System.out.println("开始生成 UserLog 数据 (" + genUserLogLen + " 条)...");
            List<Put> putList = new ArrayList<>(USER_LOG_BATCH_SIZE); // 初始化批量列表
            long startTime = System.currentTimeMillis(); // 记录开始时间

            for (int i = 0; i < genUserLogLen; i++) {
                // 随机选择一个已生成的 Video ID
                String randomVid = GenConstant.genVidInUserLog(vidList);
                // 获取该视频对应的 flip 时长
                String flip = vidMapFlip.get(randomVid);
                // 检查 flip 是否存在，避免 NullPointerException (虽然理论上应该存在)
                if (flip == null) {
                    System.err.println("警告：未找到视频 ID '" + randomVid + "' 对应的 flip 时长，跳过此条 UserLog。");
                    continue; // 跳过这条记录
                }

                // **注意:** 原代码中 videoTime 的生成逻辑看起来被覆盖了。
                // 原注释: `// videoTime则是vid对应的flip 比flip小的10~flip的数据`
                // 原代码: `UserLog userLog = UserLog.builder()...videoTime(GenConstant.genVideoTime(10)).build();`
                //         `userLog.setVideoTime(flip);` <--- 这里总是用 flip 覆盖了随机生成的时间
                // 这里我们遵循原代码的行为，将 videoTime 设置为 flip。
                // 如果需要生成小于 flip 的随机时间，需要修改 GenConstant.genVideoTime 并使用它。
                String videoWatchTime = flip; // 根据原代码逻辑，观看时长直接等于视频总时长 (flip)
                // String videoWatchTime = GenConstant.genVideoTimeWithinFlip(flip); // 如果需要小于flip的随机时间，需实现此方法

                // 创建 UserLog 对象
                // UserLog ID 使用 "userLog-" + 序号，确保唯一性
                String userLogId = "userLog-" + (i + 1);
                UserLog userLog = UserLog.builder()
                        .id(userLogId)                      // 日志唯一 ID
                        .uid(GenConstant.genAuthor(uidList)) // 随机选择一个用户 ID
                        .vid(randomVid)                    // 随机选择一个视频 ID
                        .videoTime(videoWatchTime)         // 设置观看时长 (当前等于 flip)
                        .build();
                // System.out.println(userLog); // 调试输出

                // 创建 Put 对象，RowKey 使用 "userlog-" + UserLog ID (注意大小写，原代码是小写)
                // 为了与原代码保持一致，使用 "userlog-" 前缀。更好的设计可能是直接用 UserLog ID 或复合 RowKey。
                Put put = new Put(Bytes.toBytes("userlog-" + userLog.getId()));
                // 添加列数据，列族为 "userlog"
                put.addColumn(Bytes.toBytes("userlog"), Bytes.toBytes("id"), Bytes.toBytes(userLog.getId()));
                put.addColumn(Bytes.toBytes("userlog"), Bytes.toBytes("uid"), Bytes.toBytes(userLog.getUid()));
                put.addColumn(Bytes.toBytes("userlog"), Bytes.toBytes("vid"), Bytes.toBytes(userLog.getVid()));
                put.addColumn(Bytes.toBytes("userlog"), Bytes.toBytes("videoTime"), Bytes.toBytes(userLog.getVideoTime()));

                putList.add(put); // 将 Put 对象添加到批量列表

                // 当列表达到批量大小时，提交数据并清空列表
                if (putList.size() >= USER_LOG_BATCH_SIZE) {
                    table.put(putList); // 批量提交
                    putList.clear();    // 清空列表以备下一批

                    // 更新进度条
                    printProgress(startTime, i + 1, genUserLogLen);
                }
            }

            // 循环结束后，提交剩余的 Put 数据 (如果列表不为空)
            if (!putList.isEmpty()) {
                table.put(putList);
                putList.clear();
            }

            // 确保进度条显示 100%
            printProgress(startTime, genUserLogLen, genUserLogLen);
            System.out.println("\nUserLog 数据生成并上传完毕。");

        } // try-with-resources 会在此处自动关闭 conn 和 table

        System.out.println("所有数据生成完毕！");
    }

    /**
     * 打印进度条。
     *
     * @param startTime  任务开始时间戳 (毫秒)
     * @param current    当前已处理的数量
     * @param total      总共需要处理的数量
     */
    private static void printProgress(long startTime, long current, long total) {
        long eta = current == 0 ? 0 :
                (total - current) * (System.currentTimeMillis() - startTime) / current;

        String etaHms = current == 0 ? "N/A" :
                String.format("%02d:%02d:%02d", eta / 3600000, (eta % 3600000) / 60000, (eta % 60000) / 1000);

        int percent = (int) (current * 100.0 / total);
        int barWidth = 50; // 进度条宽度
        int completedWidth = (int) (barWidth * current / total);

        StringBuilder sb = new StringBuilder();
        sb.append("\rUserLog 进度: ["); // 使用 \r 回到行首覆盖上一行
        for (int i = 0; i < barWidth; i++) {
            sb.append(i < completedWidth ? '#' : ' ');
        }
        sb.append("] ")
          .append(String.format("%3d%%", percent)) // 格式化百分比，占3位
          .append(" (")
          .append(current)
          .append("/")
          .append(total)
          .append(") ETA: ")
          .append(etaHms);

        System.out.print(sb.toString());
    }



}